﻿using System;
using System.Collections.ObjectModel;
using System.Disposables;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using Collab.Common;
using Collab.Common.Contacts;
using Collab.Common.Infrastructure;
using Collab.Services.Remote.WebserviceProxy;
using Collab.Services.Remote.WebserviceProxy.ServiceProxy;
using Microsoft.Practices.Composite.Presentation.Commands;

namespace Collab.Modules.ContactBrowser.ViewModel
{
    public class ContactsViewModel : ViewModel, IDisposable
    {
        #region Fields and properties

       private CompositeDisposable disposable = new CompositeDisposable();

        private IRepository repository;
        private MainViewModel mainViewModel;

        public event EventHandler ContactDeleted;
        public event EventHandler ContactInvitedToGroup;
        
        private SearchContactViewModel searchViewModel;
        public SearchContactViewModel SearchViewModel
        {
            get
            {
                return this.searchViewModel;
            }
            set
            {
                this.searchViewModel = value;
            }
        }

        private User currentUser;
        public User CurrentUser
        {
            get
            {
                return this.currentUser;
            }
            private set
            {
                this.currentUser = value;
            }
        }

        private Group groupDroppedTo;
        public Group GroupDroppedTo
        {
            get
            {
                return this.groupDroppedTo;
            }
            set
            {
                if (this.groupDroppedTo != value)
                {
                    this.groupDroppedTo = value;
                    NotifyPropertyChanged("GroupDroppedTo");
                }
            }

        }

        private ObservableCollection<Group> groups;
        public ObservableCollection<Group> Groups
        {
            get
            {
                return groups;
            }
            set
            {
                if (this.groups != value)
                {
                    this.groups = value;
                    NotifyPropertyChanged("Groups");
                }
            }
        }

        private Contact selectedContact;
        public Contact SelectedContact
        {
            get
            {
                return this.selectedContact;
            }
            set
            {
                if (this.selectedContact != value)
                {
                    this.selectedContact = value;
                    NotifyPropertyChanged("SelectedContact");
                }
            }
        }

        private ICommand deleteContactCommand;
        public ICommand DeleteContactCommand
        {
            get
            {
                return this.deleteContactCommand;
            }
            set
            {
                if (this.deleteContactCommand != value)
                {
                    this.deleteContactCommand = value;
                    NotifyPropertyChanged("DeleteContactCommand");
                }
            }
        }

        private ICommand inviteContactCommand;
        public ICommand InviteContactCommand
        {
            get
            {
                return this.inviteContactCommand;
            }
            set
            {
                if (this.inviteContactCommand != value)
                {
                    this.inviteContactCommand = value;
                    NotifyPropertyChanged("InviteContactCommand");
                }
            }
        }

        private ICommand joinContactCommand;
        public ICommand JoinContactCommand
        {
            get
            {
                return this.joinContactCommand;
            }
            set
            {
                if (this.joinContactCommand != value)
                {
                    this.joinContactCommand = value;
                    NotifyPropertyChanged("JoinContactCommand");
                }
            }
        }

        private ICommand inviteToGroupCommand;
        public ICommand InviteToGroupCommand
        {
            get
            {
                return this.inviteToGroupCommand;
            }
            set
            {
                if (this.inviteToGroupCommand != value)
                {
                    this.inviteToGroupCommand = value;
                    NotifyPropertyChanged("InviteToGroupCommand");
                }
            }
        }

        #endregion

        public ContactsViewModel(string username, IRepository repository, MainViewModel mainViewModel)
        {
            this.repository = repository;
            this.mainViewModel = mainViewModel;
            this.searchViewModel = new SearchContactViewModel(repository, this);

            CurrentUser = this.repository.GetUser(username);
            this.repository.SetStatus(currentUser.ID, IPUtility.GetTeredoIPv6Address(), 8888);

            LoadContacts();

            DeleteContactCommand = new DelegateCommand<Contact>(param => DeleteContact(param));
            JoinContactCommand = new DelegateCommand<Contact>(param => JoinContact(param));
            InviteContactCommand = new DelegateCommand<Contact>(param => InviteContact(param));
            InviteToGroupCommand = new DelegateCommand<Contact>(param => InviteToGroup(param));

            ContactInvitedToGroup += new EventHandler(ContactsViewModel_ContactInvitedToGroup);
            ContactDeleted += new EventHandler(ContactsViewModel_ContactDeleted);

            this.disposable.Add(Observable
                .Interval(TimeSpan.FromSeconds(10))
                .Select(x => this.repository.GetSessionInvites(this.CurrentUser.ID).FirstOrDefault())
                .Where(x => x != null)
                .Subscribe(ShowSessionInvite));
        }
        
        private void ShowSessionInvite(SessionInvite invite)
        {
            var msg = invite.Message;

            if (System.Windows.Forms.MessageBox.Show(
                String.Format("{0}", msg),
                "Join Collab Session?",
                System.Windows.Forms.MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
            {
                Messenger.Default.Send(new JoinMessage { UserName = CurrentUser.UserName, Ip = invite.InvitingIP });
            }
        }

        private void JoinContact(Contact contact)
        {
            string ip = this.repository.GetRecentOnlineStatus(contact.ID).IP;
            Messenger.Default.Send(new JoinMessage { UserName = CurrentUser.UserName, Ip = ip });
        }

        private void InviteContact(Contact contact)
        {
            // TODO: Add the ability to write a custom message (dialog box)
            Messenger.Default.Send(new HostMessage { UserName = CurrentUser.UserName});
            this.repository.CreateSessionInvite(CurrentUser.ID, contact.ID, String.Format("{0} invites you to a Collab-session.\n\nWould you like to join?", CurrentUser.UserName), IPUtility.GetTeredoIPv6Address());
            System.Windows.Forms.MessageBox.Show("The user has been invited!");
        }

        private void LoadContacts()
        {
            if (CurrentUser == null) 
                return;

            Group allContacts = new Group { GroupName = "All contacts", Users = CurrentUser.Contacts, GroupID = Guid.Empty };
            
            this.Groups = new ObservableCollection<Group>(new[]{ allContacts }.Concat(CurrentUser.Groups));
        }

        private void DeleteContact(Contact contact)
        {
            if (CurrentUser.Contacts.Contains(contact))
            {
                repository.DeleteContactAsync(contact, CurrentUser, ContactDeleted);
            }
            else
            {
                System.Windows.Forms.MessageBox.Show("The user does not exist in contact list.");
            }
        }

        void ContactsViewModel_ContactDeleted(object sender, EventArgs e)
        {
            CallbackResult<Contact> result = (CallbackResult<Contact>)sender;
            if (!result.IsError)
            {
                Application.Current.Dispatcher.BeginInvoke(new Action(() =>
                {
                    Contact contact = Groups.First(g => g.GroupID == Guid.Empty).Users.First(u => u.ID == result.Object.ID);
                    Groups.First(g => g.GroupID == Guid.Empty).Users.Remove(contact);
                    NotifyPropertyChanged("Groups");
                }));
            }
            else
            {
                System.Windows.Forms.MessageBox.Show("Failed to delete contact.");
            }
        }

        private void InviteToGroup(Contact param)
        {
            if (GroupDroppedTo != null && CurrentUser != null)
            {
                

                GroupInvite invite = new GroupInvite
                {
                    Creator = CurrentUser,
                    CreateDate = DateTime.Now,
                    Group = groupDroppedTo,
                    InvitedUser = param,
                    IsDeclined = false
                };

                repository.InivteToGroupAsync(invite, ContactInvitedToGroup);
            }
        }

        void ContactsViewModel_ContactInvitedToGroup(object sender, EventArgs e)
        {
            throw new NotImplementedException();
        }


        #region IDisposable Members

        public void Dispose()
        {
            this.disposable.Dispose();
        }

        #endregion

    }
}
